package com.biz.primus.ms.adv.service;

import com.biz.primus.base.exception.BizBusinessException;
import com.biz.primus.base.exception.BizParameterException;
import com.biz.primus.base.vo.LocationDecodeRequestVo;
import com.biz.primus.base.vo.PageVo;
import com.biz.primus.common.vo.SpringPageVO;
import com.biz.primus.model.adv.enums.AdUsePlace;
import com.biz.primus.model.adv.enums.UseType;
import com.biz.primus.model.adv.vo.AdPositionVo;
import com.biz.primus.model.adv.vo.AdvertisementQueryVo;
import com.biz.primus.model.adv.vo.AdvertisementVo;
import com.biz.primus.ms.adv.dao.po.AdPosition;
import com.biz.primus.ms.adv.dao.po.Advertisement;
import com.biz.primus.ms.adv.dao.redis.AdPositionRedisDao;
import com.biz.primus.ms.adv.dao.redis.AdvertisementRedisDao;
import com.biz.primus.ms.adv.dao.repository.AdPositionRepository;
import com.biz.primus.ms.adv.dao.repository.AdvertisementRepository;
import com.biz.primus.ms.adv.dao.ro.AdPositionRo;
import com.biz.primus.ms.adv.dao.ro.AdvertisementRo;
import com.biz.primus.ms.adv.trans.*;
import com.biz.primus.ms.base.service.AbstractBaseService;
import com.google.common.base.Predicate;
import com.google.common.base.Splitter;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.codelogger.utils.DateUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;

/**
 * @author jun.liu
 * @date 2017/9/18
 */
@Slf4j
@Service
public class AdvService extends AbstractBaseService {

    private final AdvertisementRedisDao advertisementRedisDao;

    private final AdvertisementRepository advertisementRepository;

    private final AdPositionRedisDao adPositionRedisDao;

    private final AdPositionRepository adPositionRepository;

    @Autowired
    public AdvService(AdvertisementRedisDao advertisementRedisDao, AdvertisementRepository advertisementRepository,
                      AdPositionRedisDao adPositionRedisDao, AdPositionRepository adPositionRepository) {
        this.advertisementRedisDao = advertisementRedisDao;
        this.advertisementRepository = advertisementRepository;
        this.adPositionRedisDao = adPositionRedisDao;
        this.adPositionRepository = adPositionRepository;
    }

    /**
     * 根据可用终端查询对应的广告位集合
     *
     * @param utype
     * @return
     */
    public List<AdPositionVo> queryAdByUseType(UseType utype) {
        List<Long> ids = adPositionRedisDao.findAdPositionIdsByUseType(utype);
        List<AdPositionRo> adPositionRos = adPositionRedisDao.findByIds(ids);
        return Lists.transform(adPositionRos, new AdPositionRo2AdPositionVo());
    }

    /**
     * 添加更新广告相关信息
     *
     * @param advo
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    public void addOrUpdateAdInfo(AdvertisementVo advo) {
        if (advo == null) {
            throw new BizParameterException("AdvertisementVo Parameter is null");
        }
        //默认为false 不为null和空串设置为true
        if (StringUtils.isNotBlank(advo.getSupportedGeoId())) {
            advo.setCountryStatus(true);
        }
        Advertisement advertisement = new AdvertisementVo2Advertisement().apply(advo);
        //证明是新对象请求id保存
        if (StringUtils.isBlank(advo.getId())) {
            advertisement.setId(idService.getNextId());
        }
        AdPosition adPosition = adPositionRepository.findOne(Long.parseLong(advo.getAdPositionId()));
        advertisement.setAdPosition(adPosition);

        //保存广告
        saveOrUpdateUsingPo(advertisementRepository, advertisementRedisDao, advertisement, new Advertisement2AdvertisementRo());
    }

    /**
     * 添加更新广告位信息
     *
     * @param input
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    public void addOrUpdateAdPositionInfo(AdPositionVo input) {
        if (input == null) {
            throw new BizParameterException("AdPositionVo Parameter is null");
        }
        AdPosition adPosition = new AdPositionVo2AdPosition().apply(input);
        if (adPosition == null) {
            throw new BizBusinessException("AdPosition query is null");
        }
        //证明是新对象请求id保存
        if (StringUtils.isBlank(input.getId())) {
            adPosition.setId(idService.getNextId());
        } else {//修改
            AdPosition old = adPositionRepository.findOne(Long.parseLong(input.getId()));
            if (old != null && !old.getAds().isEmpty()) {
                adPosition.setAds(old.getAds());
            }
        }
        //保存广告位
        saveOrUpdateUsingPo(adPositionRepository, adPositionRedisDao, adPosition, new AdPosition2AdPositionRo());
    }

    /**
     * 根据id删除广告
     *
     * @param id
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    public void deleteAdById(long id) {
        delayer.executeAfterTransactionCommit(() -> advertisementRedisDao.delete(id));
        advertisementRepository.delete(id);
    }

    /**
     * 根据广告位id 删除广告和广告位
     *
     * @param id
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    public void deleteAdPositionById(long id) {
        AdPosition adPosition = adPositionRepository.findOne(id);
        if (adPosition == null){
            throw new BizBusinessException("AdPosition query is null");
        }

        if(adPosition.getAds() != null && !adPosition.getAds().isEmpty()){
            adPosition.getAds().forEach(advertisement -> {
                advertisementRedisDao.delete(advertisement.getId());
            });
            advertisementRepository.delete(adPosition.getAds());
        }

        delayer.executeAfterTransactionCommit(() -> adPositionRedisDao.delete(id));
        adPositionRepository.delete(id);
    }

    /**
     * 根据广告位id获取对应的广告
     *
     * @param positionId
     * @return
     */
    public List<AdvertisementVo> getAdsByPositionId(long positionId) {
        if (positionId == 0) {
            throw new BizParameterException("positionId Parameter is null");
        }
        AdPositionRo adPositionRo = adPositionRedisDao.findOne(positionId);
        if (adPositionRo != null && adPositionRo.getAdvertisementId() != null) {
            String advertisementIdStr = adPositionRo.getAdvertisementId();
            String[] str = advertisementIdStr.split(",");
            List<AdvertisementRo> advertisementRos = advertisementRedisDao.findByStrIds(Arrays.asList(str));
            return Lists.transform(advertisementRos, new AdvertisementRo2AdvertisementVo());
        }
        return new ArrayList<AdvertisementVo>();
    }

    /**
     * 获取所有的广告位
     *
     * @return
     */
    public List<AdPositionVo> getAdPositions() {
        return Lists.transform(adPositionRedisDao.findAll(), new AdPositionRo2AdPositionVo());
    }

    /**
     * 根据广告位id获取广告位
     *
     * @param positionId
     * @return
     */
    public AdPositionVo getAdPositionById(long positionId) {
        if (positionId == 0) {
            throw new BizParameterException("positionId Parameter is null");
        }
        AdPositionRo adPositionRo = adPositionRedisDao.findOne(positionId);
        if (adPositionRo == null) {
            throw new BizBusinessException("AdPositionRo query is null");
        }
        AdPositionVo adPositionVo = new AdPositionRo2AdPositionVo().apply(adPositionRo);
        if (StringUtils.isNotBlank(adPositionRo.getAdvertisementId())) {
            List<AdvertisementVo> list = new ArrayList<>();
            String[] str = adPositionRo.getAdvertisementId().split(",");
            for (String id : str) {
                AdvertisementRo advertisementRo = advertisementRedisDao.findOne(Long.valueOf(id));
                list.add(new AdvertisementRo2AdvertisementVo().apply(advertisementRo));
            }
            adPositionVo.setAdvertisementVos(list);
        }
        return adPositionVo;
    }

    /**
     * 根据广告id获取广告
     *
     * @param adId
     * @return
     */
    public AdvertisementVo getAdById(long adId) {
        AdvertisementRo advertisementRo = advertisementRedisDao.findOne(adId);
        if (advertisementRo == null) {
            throw new BizBusinessException("AdvertisementRo query is null");
        }
        return new AdvertisementRo2AdvertisementVo().apply(advertisementRo);
    }

    public List<AdvertisementVo> getAllAds() {
        List<AdvertisementRo> advertisementRos = advertisementRedisDao.findAll();
        return Lists.transform(advertisementRos, new AdvertisementRo2AdvertisementVo());
    }

    /**
     * 根据广告位id分页查询广告 如果广告位id为null则分页查询所有广告
     *
     * @param queryVo
     * @return
     */
    public SpringPageVO<AdvertisementVo> getAdsByParams(AdvertisementQueryVo queryVo) {
        if (queryVo == null) {
            throw new BizParameterException("AdvertisementQueryVo Parameter is null");
        }
        PageRequest pageRequest = queryVo.toPageRequest();
        Page<Advertisement> page;

        if (queryVo.getAdPositionId() != 0) {
            page = this.advertisementRepository.findAll(queryVo.getAdPositionId(), pageRequest);
        } else {
            page = this.advertisementRepository.findAll(pageRequest);
        }

        if (page == null) {
            throw new BizBusinessException("page result is null");
        }
        List<AdvertisementVo> itemVos = Lists.transform(page.getContent(), new Advertisement2AdvertisementVo());
        return new SpringPageVO.Builder<AdvertisementVo>().content(itemVos).number(queryVo.getPage()).size(itemVos.size()).totalElements(page.getTotalElements()).build();
    }

    /**
     * 分页查询广告位
     *
     * @param queryVo
     * @return
     */
    public SpringPageVO<AdPositionVo> getAllAdPositions(PageVo queryVo) {
        if (queryVo == null) {
            throw new BizParameterException("PageVo Parameter is null");
        }
        PageRequest pageRequest = queryVo.toPageRequest();
        Page<AdPosition> page = this.adPositionRepository.findAll(pageRequest);
        if (page == null) {
            throw new BizBusinessException("page result is null");
        }
        List<AdPositionVo> itemVos = Lists.transform(page.getContent(), new AdPosition2AdPositionVo());
        return new SpringPageVO.Builder<AdPositionVo>().content(itemVos).number(queryVo.getPage()).size(itemVos.size()).totalElements(page.getTotalElements()).build();
    }

    /**
     * 根据经纬度  广告可用类型查询广告位
     *
     * @param latitude
     * @param longitude
     * @param adUsePlace
     * @return
     */
    public AdPositionVo findAdByProvinceId(BigDecimal latitude, BigDecimal longitude, AdUsePlace adUsePlace) {
        log.debug("经度：" + longitude + ",纬度：" + latitude);
        //如果没传经纬度默认查全国
        if (latitude == null || longitude == null) {
            return this.findAdpositionCountry(adUsePlace);
        }
        //按省ID查
        LocationDecodeRequestVo vo = new LocationDecodeRequestVo();
        vo.setLatitude(latitude);
        vo.setLongitude(longitude);

//TODO 暂时获取的是省id 后续再todo
//引用geoService   CityItemResponseVo后打开
//        CityItemResponseVo cityItemResponseVo = geoService.decodeLocation(vo);
        Integer provinceId = 32;
//        if (cityItemResponseVo != null) {
//            provinceId = cityItemResponseVo.getProvinceId();
//            log.debug("省ID为：" + provinceId);
//        }

        Long adPositionId = adPositionRedisDao.findAdPositionByUsePlace(adUsePlace);
        if (adPositionId == null) {
            log.debug("根据{}查询不到广告位信息", adUsePlace);
            throw new BizBusinessException("adPositionId result query is null");
        }
        //查询出广告位
        AdPositionRo adPositionRo = adPositionRedisDao.findOne(adPositionId);
        if (StringUtils.isNotBlank(adPositionRo.getAdvertisementId()) && adPositionRo.isInvalid() == true) {
            //查询广告位对于的广告
            List<String> strId = Splitter.on(",").splitToList(adPositionRo.getAdvertisementId());
            List<AdvertisementRo> advertisementRos = advertisementRedisDao.findByStrIds(strId);
            List<AdvertisementVo> advertisementVos = Lists.transform(advertisementRos, new AdvertisementRo2AdvertisementVo());
            List<AdvertisementVo> advertisementVoList = new ArrayList<>();
            for (AdvertisementVo adVo : advertisementVos) {
                if (StringUtils.isNotBlank(adVo.getSupportedGeoId())) {
                    List<String> strProvinceId = Splitter.on(",").splitToList(adVo.getSupportedGeoId());
                    for (String str : strProvinceId) {
                        if (provinceId == Integer.parseInt(str)) {
                            advertisementVoList.add(adVo);
                        }
                    }
                }
            }
            //查省查不到查全国
            if (advertisementVoList.size() <= 0) {
                return this.findAdpositionCountry(adUsePlace);
            } else {
                AdPositionVo adPositionVo = new AdPositionRo2AdPositionVo().apply(adPositionRo);
                Iterable<AdvertisementVo> resp = Iterables.filter(advertisementVoList, new Predicate<AdvertisementVo>() {
                    @Override
                    public boolean apply(AdvertisementVo input) {
                        return isDateBetween(new Date(System.currentTimeMillis()), DateUtils.getDateFromString(input.getBeginDate(), "yyyy-MM-dd"),
                                DateUtils.getDateFromString(input.getEndDate(), "yyyy-MM-dd"));

                    }
                });
                adPositionVo.setAdvertisementVos(Lists.newArrayList(resp));
                return adPositionVo;
            }
        }
        return new AdPositionVo();
    }

    /**
     * 比较compare日期是否在start和end之间，一个为null就返回false
     *
     * @param compare
     * @param start
     * @param end
     * @return
     */
    private static boolean isDateBetween(Date compare, Date start, Date end) {
        if (compare == null || start == null || end == null) {
            return false;
        }
        long cl = compare.getTime();
        long sl = start.getTime();
        long el = end.getTime();
        return cl >= sl && cl <= el;
    }

    /**
     * 根据可用类型查询广告位
     *
     * @param adUsePlace
     * @return
     */
    public AdPositionVo getAdpositionCountry(AdUsePlace adUsePlace) {
        return this.findAdpositionCountry(adUsePlace);
    }

    /**
     * 根据可用类型查询广告位是否存在
     *
     * @param adUsePlace
     * @return
     */
    public boolean findAdPositionByUsePlace(AdUsePlace adUsePlace) {
        if (adPositionRedisDao.findAdPositionByUsePlace(adUsePlace) != null) {
            //存在
            return true;
        } else {
            //不存在
            return false;
        }
    }

    /**
     * 根据可用类型查询广告位
     *
     * @param adUsePlace
     * @return
     */
    public AdPositionVo findAdpositionCountry(AdUsePlace adUsePlace) {
        List<AdvertisementVo> advertisementVoList = new ArrayList<>();
        Long adPositionId = adPositionRedisDao.findAdPositionByUsePlace(adUsePlace);
        if (adPositionId == null) {
            log.debug("根据{}查询不到广告位信息", adUsePlace);
            throw new BizBusinessException("adPositionId result query is null");
        }
        //查询出广告位
        AdPositionRo adPositionRo = adPositionRedisDao.findOne(adPositionId);
        AdPositionVo adPositionVo = new AdPositionVo();
        if (StringUtils.isNotBlank(adPositionRo.getAdvertisementId())) {
            //查询广告位对于的广告
            List<String> strId = Splitter.on(",").splitToList(adPositionRo.getAdvertisementId());
            List<AdvertisementRo> advertisementRos = advertisementRedisDao.findByStrIds(strId);
            List<AdvertisementVo> advertisementVos = Lists.transform(advertisementRos, new AdvertisementRo2AdvertisementVo());
            for (AdvertisementVo vo : advertisementVos) {
                if (vo.isCountryStatus()) {
                    advertisementVoList.add(vo);
                }
            }
            adPositionVo = new AdPositionRo2AdPositionVo().apply(adPositionRo);

            Iterable<AdvertisementVo> resp = Iterables.filter(advertisementVoList, new Predicate<AdvertisementVo>() {
                @Override
                public boolean apply(AdvertisementVo input) {
                    return isDateBetween(new Date(System.currentTimeMillis()), DateUtils.getDateFromString(input.getBeginDate(), "yyyy-MM-dd"),
                            DateUtils.getDateFromString(input.getEndDate(), "yyyy-MM-dd"));
                }
            });

            adPositionVo.setAdvertisementVos(Lists.newArrayList(resp));
        }
        return adPositionVo;
    }

}
